﻿using System;
using System.Collections.Generic;
using System.IO;

using Marx.LitJson;

namespace Marx
{

    public static class JsonDataUtils
    {
        public static JsonData GetOrDefault(this JsonData data, string key)
        {
            if (data.Keys.Contains(key))
            {
                return data[key];
            }
            else
            {
                return null;
            }
        }

        public static T[] Load<T>(JsonData data) where T : IConfigObject, new()
        {
            List<T> array = new List<T>();
            foreach (JsonData item in data)
            {
                var t = new T();
                t.Read(item);
                array.Add(t);
                if (t is IDelayInit delayInit)
                {
                    ConfigDelayInit.AddDelayAction(delayInit.OnDelayInit);
                }
            }

            return array.ToArray();
        }

        public static T LoadSingle<T>(JsonData data) where T : IConfigObject, new()
        {
            var t = new T();
            t.Read(data);
            return t;
        }
    }

    public static class ConfigDelayInit
    {
        private static List<Action> delayInitActions_;

        public static void AddDelayAction(Action action)
        {
            if (delayInitActions_ == null)
            {
                delayInitActions_ = new List<Action>();
            }
            delayInitActions_.Add(action);
        }

        public static void InvokeDelayInitAction()
        {
            while (delayInitActions_ != null && delayInitActions_.Count > 0)
            {
                var list = delayInitActions_;
                delayInitActions_ = null;
                foreach (Action action in list)
                {
                    action();
                }
            }
            delayInitActions_ = null;
        }
    }

    public interface IConfigObject
    {
        void Read(JsonData item);
    }

    public interface IDelayInit
    {
        void OnDelayInit();
    }

    public static class ConfigElementReader
    {
        public static int Get(JsonData element, string name, int _)
        {
            return Convert(element.GetOrDefault(name), _);
        }

        public static double Get(JsonData element, string name, double _)
        {
            return Convert(element.GetOrDefault(name), _);
        }

        public static string Get(JsonData element, string name, string _)
        {
            return Convert(element.GetOrDefault(name), _);
        }

        public static bool Get(JsonData element, string name, bool _)
        {
            return Convert(element.GetOrDefault(name), _);
        }

        public static T Get<T>(JsonData element, string name, T _) where T : IConfigObject, new()
        {
            return Convert_T(element.GetOrDefault(name), _);
        }

        public static int[] GetArray(JsonData element, string itemName, int[] _)
        {
            return _GetArray<int>(element, itemName, Convert);
        }

        public static double[] GetArray(JsonData element, string itemName, double[] _)
        {
            return _GetArray<double>(element, itemName, Convert);
        }

        public static string[] GetArray(JsonData element, string itemName, string[] _)
        {
            return _GetArray<string>(element, itemName, Convert);
        }

        public static T[] GetArray<T>(JsonData element, string itemName, T[] _) where T : IConfigObject, new()
        {
            return _GetArray_T<T>(element, itemName, Convert_T);
        }

        private static T[] _GetArray<T>(JsonData element, Func<JsonData, T, T> convert)
        {
            List<T> list = new List<T>();
            foreach (JsonData node in element)
            {
                list.Add(convert(node, default(T)));
            }
            return list.Count > 0 ? list.ToArray() : null;
        }

        private static T[] _GetArray<T>(JsonData element, string itemName, Func<JsonData, T, T> convert)
        {
            var listNode = element.GetOrDefault(itemName);
            if (listNode != null)
            {
                return _GetArray(listNode, convert);
            }
            return null;
        }

        private static T[] _GetArray_T<T>(JsonData element, Func<JsonData, T, T> convert) where T : new()
        {
            List<T> list = new List<T>();
            foreach (JsonData node in element)
            {
                list.Add(convert(node, new T()));
            }
            return list.Count > 0 ? list.ToArray() : null;
        }

        private static T[] _GetArray_T<T>(JsonData element, string itemName, Func<JsonData, T, T> convert) where T : new()
        {
            var listNode = element.GetOrDefault(itemName);
            if (listNode != null)
            {
                return _GetArray_T(listNode, convert);
            }
            return null;
        }

        private static int Convert(JsonData e, int _)
        {
            if (e == null)
            {
                return 0;
            }
            return Convert(e.ToString(), _);
        }

        private static double Convert(JsonData e, double _)
        {
            if (e == null)
            {
                return 0.0;
            }
            return Convert(e.ToString(), _);
        }

        private static string Convert(JsonData e, string _)
        {
            if (e == null)
            {
                return "";
            }
            return Convert(e.ToString(), _);
        }

        private static bool Convert(JsonData e, bool _)
        {
            if (e == null)
            {
                return false;
            }
            return Convert(e.ToString(), _);
        }

        private static T Convert_T<T>(JsonData e, T _) where T : IConfigObject, new()
        {
            if (e == null)
            {
                return default(T);
            }

            T t = new T();
            t.Read(e);

            if (t is IDelayInit delayInit)
            {
                ConfigDelayInit.AddDelayAction(delayInit.OnDelayInit);
            }
            return t;
        }

        private static int Convert(string s, int _)
        {
            if (string.IsNullOrEmpty(s))
            {
                return 0;
            }

            return int.Parse(s);
        }

        private static double Convert(string s, double _)
        {
            if (string.IsNullOrEmpty(s))
            {
                return 0.0;
            }

            return double.Parse(s);
        }

        private static string Convert(string s, string _)
        {
            return s;
        }

        private static bool Convert(string s, bool _)
        {
            if (string.IsNullOrEmpty(s))
            {
                return false;
            }
            return bool.Parse(s);
        }
    }
}